package kz.kstu.aec;

import kz.kstu.aec.dsa.DSAHelper;
import kz.kstu.aec.model.*;
import kz.kstu.aec.model.Point2D;
import kz.kstu.aec.util.CryptographyUtil;
import sun.security.ec.ECPrivateKeyImpl;
import sun.security.ec.ECPublicKeyImpl;

import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;

/**
 * @author Filipp_Stankevich
 *         created: 5/2/12 11:18 AM
 */
public class Authenticator {

    private static Authenticator instance;

    private Authenticator() {
    }

    public static Authenticator getInstance() {
        if (instance == null) {
            instance = new Authenticator();
        }
        return instance;
    }

    public AuthenticationData getAuthenticationData(ClientData clientData, BigInteger serverNumber) {
        if (serverNumber == null) {
            throw new IllegalArgumentException("server number can not be null");
        }

        BigInteger clientNumber = CryptographyUtil.getRandomNumber();
        String login = clientData.getLogin();
        String password = clientData.getPassword();
        ECPrivateKeyImpl privateKey = DSAHelper.getPrivateKey(login, password);
        BigInteger message = serverNumber.add(clientNumber);
        ECSignature signature = DSAHelper.sign(message.toByteArray(), privateKey);
        return new AuthenticationData(login, signature, clientNumber);
    }

    public Point2D getPublicKey(ClientData clientData) {
        String login = clientData.getLogin();
        String password = clientData.getPassword();
        ECPrivateKeyImpl privateKey = DSAHelper.getPrivateKey(login, password);
        ECPublicKeyImpl ecPublicKey = DSAHelper.generatePublicKey(privateKey);
        ECPoint publicPoint = ecPublicKey.getW();
        return new Point2D(publicPoint.getAffineX(), publicPoint.getAffineY());
    }


    public boolean checkAuthenticationData(AuthenticationData authenticationData, ServerData serverData) {
        BigInteger authenticationNumber = serverData.getAuthenticationNumber();
        BigInteger message = authenticationNumber.add(authenticationData.getClientNumber());
        ECParameterSpec ecParameterSpec = kz.kstu.aec.curve.factory.EllipticCurveFactory.getEllipticCurveSpec();
        Point2D publicPoint = serverData.getPublicPoint();
        try {
            ECPoint ecPoint = new ECPoint(publicPoint.getX(), publicPoint.getY());
            ECPublicKeyImpl ecPublicKey = new ECPublicKeyImpl(ecPoint, ecParameterSpec);
            return DSAHelper.checkSignature(authenticationData.getSignature(), ecPublicKey, message.toByteArray());
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("Internal error. Can not apply a public key.");
        }
    }

    public BigInteger generateAuthenticationNumber() {
        return CryptographyUtil.getRandomNumber().abs();
    }
}
